import random
import math
import pandas
import pandas as pd
import numpy as np

class KMeans:
    def __init__(self, n_clusters=3, max_iter=300):
        self.n_clusters = n_clusters
        self.max_iter = max_iter
        self.centroids = None
    def fit(self, X):
        n_samples, n_features = X.shape
        # 随机初始化聚类中心
        self.centroids= X[random.sample(range(n_samples), self.n_clusters), :]

        # 开始迭代
        for i in range(self.max_iter):
            # 计算每个样本到聚类中心的距离，并分配到最近的聚类中心所在的簇
            labels = self._compute_labels(X, self.centroids)

            # 更新聚类中心
            new_centroids = self._compute_centroids(X, labels)

            # 判断是否收敛
            if self._is_converged(self.centroids, new_centroids):
                break

            self.centroids = new_centroids

        return self.centroids, labels

    def _compute_labels(self, X, centroids):
        # 计算每个样本到聚类中心的距离，并分配到最近的聚类中心所在的簇
        labels = []
        for i in range(len(X)):
            distances = [self._euclidean_distance(X[i], c) for c in centroids]
            cluster = distances.index(min(distances))
            labels.append(cluster)
        return labels

    def _compute_centroids(self, X, labels):
        # 更新聚类中心
        new_centroids = []
        for i in range(self.n_clusters):
            cluster = [X[j] for j in range(len(X)) if labels[j] == i]
            centroid = [sum(x) / len(x) for x in zip(*cluster)]
            new_centroids.append(centroid)
        return new_centroids

    def _is_converged(self, centroids, new_centroids):
        # 判断是否收敛
        distances = [self._euclidean_distance(centroids[i], new_centroids[i]) for i in range(self.n_clusters)]
        return sum(distances) == 0

    def _euclidean_distance(self, x1, x2):
        # 计算欧几里得距离
        distance = 0
        for i in range(len(x1)):
            distance += pow((x1[i] - x2[i]), 2)
        return math.sqrt(distance)

    def predict(self,X):
        if self.centroids!=None:
            labels = self._compute_labels(X, self.centroids)
            return labels
        else:
            print("error! please fit firstly!!")

data = pandas.read_csv('Iris.csv')

data = data.iloc[:,1:6]

# 打乱数据顺序
data = data.sample(frac=1)
# 计算分割点
split_point = int(len(data) * 0.8)
# 分割数据集
train_data = data.iloc[:split_point,0:4]
test_data = data.iloc[split_point:,0:4]

test_data_cmp = data.iloc[split_point:,4]
kmean=KMeans(n_clusters=3)

kmean.fit(np.array(train_data))
label = kmean.predict(np.array(test_data))
result=np.column_stack((test_data_cmp, label))

print('最后聚类的结果为：',result)